class GameEngineSystem {

    onFrame(advancedTime) {

    }

    onRecieveEvent(eventName: string) {

    }
}

class GameEngineMouseListenerSystem extends GameEngineSystem {

    currentEvent = null;

    constructor() {
        super();
        window.addEventListener('click', (e) => {
            this.currentEvent = e;
        });
    }

    onClick(e) {
        const mouseGlobalX = e.offsetX;
        const mouseGlobalY = e.offsetY;
        const point = { x: mouseGlobalX, y: mouseGlobalY };
        let result = this.hitTest(core.root, point);
        while (result) {
            if (result.onClick) {
                result.onClick();
            }
            result = result.parent;
        }
    }

    hitTest(gameObject, point) {
        if (gameObject.renderer) {
            //console.log(gameObject.renderer);
            return gameObject.renderer.hitTest(point);
        }
        else {
            for (let i = gameObject.children.length - 1; i >= 0; i--) {
                const child = gameObject.children[i];
                const childTransform = child.getBehaviour(Transform);
                const localMatrix = childTransform.$localMatrix;
                const invertLocalMatrix = invertMatrix(localMatrix);
                //相对于子对象的相对坐标 = 相对于当前对象的相对坐标 * 子对象的相对矩阵的逆矩阵
                const childLocalPoint = pointAppendMatrix(point, invertLocalMatrix);
                const result = this.hitTest(child, childLocalPoint)
                if (result) {
                    return result;
                }
            }
            return null;
        }
    }

    onFrame(advancedTime) {
        if (this.currentEvent) {
            this.onClick(this.currentEvent);
            this.currentEvent = null;
        }
    }
}

class GameEngineRenderSystem extends GameEngineSystem {

    onFrame(advancedTime) {
        this.gameEngineDraw(core.root, context)
    }

    gameEngineDraw(gameObject, context) {
        const transform = gameObject.getBehaviour(Transform);
        //console.log(gameObject);
        transform.calculateGlobalProperties();
        context.setTransform(
            transform.$globalMatrix.a,
            transform.$globalMatrix.b,
            transform.$globalMatrix.c,
            transform.$globalMatrix.d,
            transform.$globalMatrix.tx,
            transform.$globalMatrix.ty
        )
        context.globalAlpha = transform.$globalAlpha;

        if (gameObject.visible) {
            if (gameObject.renderer) {
                gameObject.renderer.onDraw(context);
            }
            for (let child of gameObject.children) {
                this.gameEngineDraw(child, context);
            }
        }
    }
}

class GameEnginePlayModeLifeCycleSystem extends GameEngineSystem {


    lastIterationTime = 0;

    lastAdvancedTime = 0;

    onFrame(advancedTime: number) {
        const duringTime = advancedTime - this.lastAdvancedTime;
        this.lastAdvancedTime = advancedTime;
        this.lastIterationTime += duringTime;
        const frameTime = 1000 / 60;
        while (this.lastIterationTime >= frameTime) {
            this.lastIterationTime -= frameTime;

            this.gameObjectOnStart(core.root);
            this.gameObjectOnUpdate(core.root, duringTime);
            this.gameObjectOnDestroy(core.root);
        }
    }

    gameObjectOnStart(gameObject: GameObject) {
        for (const behaviour of gameObject.$behaviours) {
            if (!behaviour.$isExecuteOnStart) {
                behaviour.onStart();
                behaviour.$isExecuteOnStart = true;
            }
            for (let child of gameObject.children) {
                this.gameObjectOnStart(child)
            }
        }
    }

    gameObjectOnUpdate(gameObject: GameObject, duringTime: number) {
        for (const behaviour of gameObject.$behaviours) {
            behaviour.onUpdate(duringTime)
        }
        for (let child of gameObject.children) {
            this.gameObjectOnUpdate(child, duringTime)
        }
    }

    gameObjectOnDestroy(gameObject: GameObject) {
        const newBehaviours = [];
        for (const behaviour of gameObject.$behaviours) {
            if (behaviour.$isDestroy) {
                behaviour.onDestroy();
            }
            else {
                newBehaviours.push(behaviour);
            }
        }
        gameObject.$behaviours = newBehaviours;
    }
}

class GameEngineCollisionDetectionSystem extends GameEngineSystem {


    gameType = "";

    object = null;
    info = {
        position: { x: 3, y: 4 },
        data: [
            [1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 0, 0, 0, 0, 0, 0, 0, 1],
            [1, 0, 0, 1, 0, 0, 0, 0, 1],
            [1, 0, 0, 1, 0, 0, 0, 0, 1],
            [1, 1, 0, 0, 0, 1, 1, 0, 1],
            [1, 0, 0, 1, 1, 0, 0, 0, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1],
            [1, 1, 1, 1, 1, 1, 1, 1, 1]
        ]
    };
    result = {};

    //  0  1  2  3  4  5  6  7  8
    //
    // [1, 1, 1, 1, 1, 1, 1, 1, 1],   0
    // [1, 0, 0, 0, 0, 0, 0, 0, 1],   1
    // [1, 0, 0, 0, 0, 0, 0, 0, 1],   2
    // [1, 0, 0, 2, 2, 2, 0, 0, 1],   3
    // [1, 0, 0, 2, 2, 2, 0, 0, 1],   4
    // [1, 0, 0, 0, 0, 0, 0, 0, 1],   5
    // [1, 1, 1, 1, 1, 1, 1, 1, 1]    6



    onFrame() {

        if (this.object) {
            this.result = this.detecFunction(this.object, this.info);
            //this.detecFunction(this.object2, this.info);
            //console.log(this.result)
        }

    }

    detecFunction(object, info) {

        let left = this.checkLeft(object, info);
        let right = this.checkRight(object, info);
        let up = this.checkUp(object, info);
        let down = this.checkDown(object, info);

        //console.log(down)

        return {
            left: left,
            right: right,
            up: up,
            down: down
        }
    }

    checkLeft(object, info) {
        // for (let i = 0; i < object.length; i++) {
        //     let j = 0;
        //     while (object[i][j] !== 1) {
        //         j++;
        //     }
        //     if (info.data[info.position.y + i][info.position.x + j - 1] == 1) {
        //         return false;
        //     }
        // }
        // return true;

        let otherObj = true;

        if (this.gameType == "tuixiangzi") {
            if (info.data[info.position.y][info.position.x - 2] == 1) {
                otherObj = false;
            }
        }

        for (let i = 0; i < object.height; i++) {
            if (info.data[info.position.y + i][info.position.x - 1] == 1) {
                return {
                    thisObject: false,
                    otherObject: otherObj
                };
            }
        }
        return {
            thisObject: true,
            otherObject: otherObj
        };
    }

    checkRight(object, info) {
        // for (let i = 0; i < object.length; i++) {
        //     let j = object[0].length;
        //     while (object[i][j] !== 1) {
        //         j--;
        //     }
        //     if (info.data[info.position.y + i][info.position.x + j + 1] == 1) {
        //         return false;
        //     }
        // }
        // return true;

        let otherObj = true;

        if (this.gameType == "tuixiangzi") {
            if (info.data[info.position.y][info.position.x + 2] == 1) {
                otherObj = false;
            }
        }

        for (let i = 0; i < object.height; i++) {
            if (info.data[info.position.y + i][info.position.x + object.length] == 1) {
                return {
                    thisObject: false,
                    otherObject: otherObj
                };
            }
        }
        return {
            thisObject: true,
            otherObject: otherObj
        };
    }

    checkUp(object, info) {
        // for (let j = 0; j < object[0].length; j++) {
        //     let i = 0;
        //     while (object[i][j] !== 1) {
        //         i++;
        //     }
        //     if (info.data[info.position.y + i - 1][info.position.x + j] == 1) {
        //         return false;
        //     }
        // }
        // return true;

        let otherObj = true;

        if (this.gameType == "tuixiangzi") {
            if (info.data[info.position.y - 2][info.position.x] == 1) {
                otherObj = false;
            }
        }

        for (let i = 0; i < object.length; i++) {
            if (info.data[info.position.y - 1][info.position.x + i] == 1) {
                return {
                    thisObject: false,
                    otherObject: otherObj
                };
            }
        }
        return {
            thisObject: true,
            otherObject: otherObj
        };
    }

    checkDown(object, info) {
        // for (let j = 0; j < object[0].length; j++) {
        //     let i = object.length - 1;
        //     while (object[i][j] !== 1) {
        //         i--;
        //     }
        //     if (info.data[info.position.y + i + 1][info.position.x + j] == 1) {
        //         return false;
        //     }
        // }
        // return true;

        let otherObj = true;

        if (this.gameType == "tuixiangzi") {
            if (info.data[info.position.y + 2][info.position.x] == 1) {
                otherObj = false;
            }
        }

        for (let i = 0; i < object.length; i++) {
            if (info.data[info.position.y + object.height][info.position.x + i] == 1) {
                return {
                    thisObject: false,
                    otherObject: otherObj
                };
            }
        }
        return {
            thisObject: true,
            otherObject: otherObj
        };
    }

    setData(object, position, data) {
        this.object = object;
        this.info.position = position;
        this.info.data = data;
    }

    clearData() {
        this.object = null;
        this.info = {
            position: { x: 0, y: 0 },
            data: []
        };
        this.result = [];
    }
}

class EditorApiSystem extends GameEngineSystem {

    private isFirst: boolean = true;

    private currentSceneUrl = './data/data_test.json';

    private flag = 1;

    onFrame() {
        if (this.isFirst) {
            this.isFirst = false;

            nativeMenu.run(this);
            hierachyPanel.run(this);
            insepectorPanel.run(this);
            resourcePanel.run(this);

            // const hierarchyData = getHierarchyData(core.root);
            // hierachyPanel.update(hierarchyData);


        }
    }

    executeCommand(name: string, param: any) {
        //console.log('执行命令', name, '参数', param);
        switch (name) {
            case "SelectGameObject":
                const gameObject = GameObject.getGameObject(param);
                const gameObjectData = getInspectorData(gameObject);
                insepectorPanel.update(gameObjectData);
                break;
            case "ModifyBehaviourProperty":
                // const behaviour = Behaviour.getBehaviour(param.uuid);
                // behaviour[param.key] = param.newValue;
                // if(behaviour.isChangableValueForPrefab){

                //     //调用onValueChange()
                //     behaviour.onAddToGameObject();

                // }
                this.setInspectorData(param.objName, param.behaviourId, param.behaviourName, param.key, param.newValue);
                hierachyPanel.update(getHierarchyData(core.root));
                // const obj = GameObject.getGameObject(param.objId);
                // insepectorPanel.update(getInspectorData(obj));
                break;
            case "Save":
                const data = core.toData(core.root.children[0]);
                const content = JSON.stringify(data, null, '\t');
                const fs = require('fs');
                //fs.writeFileSync('./data/data.json', content, 'utf-8')
                //console.log(data);
                fs.writeFileSync(this.currentSceneUrl, content, 'utf-8');
                loadText(this.currentSceneUrl, () => {
                    console.log("reload: " + this.currentSceneUrl);
                })
                break;
            case "ChangeScene":
                this.currentSceneUrl = param;
                core.changeScene(this.currentSceneUrl);
                break;
            case "AddBehaviourOnGameObject":
                const { uuid, behaviourName } = param;
                const gameObject1 = GameObject.getGameObject(uuid);
                const behaviourClass = core.getBehaviourClass(behaviourName)
                gameObject1.addBehaviour(new behaviourClass());
                insepectorPanel.update(getInspectorData(gameObject1));
                break;
            case "RemoveBehaviour":
                const behaviourId = param.uuid;
                const gameObject2 = param.gameObject;
                //console.log(gameObject2);
                gameObject2.deleteBehaviour(behaviourId);
                insepectorPanel.update(getInspectorData(gameObject2));
                break;
            case "AddGameObject":
                const gameObject3 = GameObject.getGameObject(param);
                const newGameObject = new GameObject();
                newGameObject.addBehaviour(new Transform());
                gameObject3.addChild(newGameObject);
                console.log(gameObject3);
                hierachyPanel.update(getHierarchyData(core.root));
                break;
            case "DeleteGameObject":
                const gameObject4 = GameObject.getGameObject(param);
                gameObject4.parent.removeChild(gameObject4);
                hierachyPanel.update(getHierarchyData(core.root));
                break;
            case "CreateScene":
                this.currentSceneUrl = "./data/new_scene(" + (this.flag++) + ").json";
                core.configData.scenes.push(this.currentSceneUrl);
                const content2 = JSON.stringify(core.configData, null, '\t');
                const fs2 = require('fs');
                fs2.writeFileSync("./data/config.json", content2, 'utf-8');

                const content3 = JSON.stringify({
                    "id": "gameObject",
                    "behaviours": [
                        {
                            "name": "Transform"
                        }
                    ]
                }, null, '\t');
                const fs3 = require('fs');
                fs3.writeFileSync(this.currentSceneUrl, content3, 'utf-8');

                loadText(this.currentSceneUrl, () => {
                    this.executeCommand("ChangeScene", this.currentSceneUrl);
                    resourcePanel.run(this);
                })

                //this.executeCommand("Save", null);
                break;
            case "DeleteScene":
                for (let i = 0; i < core.configData.scenes.length; i++) {
                    console.log(this.currentSceneUrl + "  " + core.configData.scenes[i])
                    if (this.currentSceneUrl == core.configData.scenes[i]) {
                        core.configData.scenes.splice(i, 1);
                        const content = JSON.stringify(core.configData, null, '\t');
                        const fs = require('fs');
                        fs.writeFileSync("./data/config.json", content, 'utf-8');

                        // var objFSO = new ActiveXObject("Scripting.FileSystemObject");
                        // objFSO.DeleteFile(this.currentSceneUrl, true);
                        this.currentSceneUrl = core.configData.scenes[0];
                        this.executeCommand("ChangeScene", this.currentSceneUrl);
                        resourcePanel.run(this);

                    }

                }
                break;
        }
    }


    setInspectorData(name: string, behaviourId: number, behaviourName: string, key: string, value: any) {

        const gameObject = core.getObjectById(name);

        if (gameObject.prefabUrl) {
            const objBehaviour = gameObject.getBehaviour(ChangableValueForPrefab);
            for (let item of objBehaviour.properties) {
                //console.log(Behaviour.getBehaviour(behaviourId));
                if (item.name == behaviourName) {
                    //console.log(behaviourId);
                    item.parameter[key] = value;
                    break;
                }
            }
            objBehaviour.onValueChange();
            // console.log(gameObject.getBehaviour(Transform));
        } else {
            const objBehaviour = Behaviour.getBehaviour(behaviourId);
            objBehaviour[key] = value;
            objBehaviour.onValueChange();
        }


    }

    onRecieveEvent(eventName: string) {
        if (eventName == 'sceneChanged') {
            const hierarchyData = getHierarchyData(core.root);
            hierachyPanel.update(hierarchyData);
            insepectorPanel.clear();
        }
    }
}

